<template>
  <view
    class="w-select"
    id="wSelect"
    :style="{
      '--select-wrap-width': width,
      '--select-wrap-height': height,
      '--select-bg-color': bgColor,
    }"
  >
    <view
      :class="isShow ? 'select-wrap-active' : ''"
      class="select-wrap"
      @click="changeShow"
    >
      <view v-if="multiple" class="select-content">
        <view
          class="select-content-item-default"
          v-if="multiSelectList.length === 0 && !filterable"
        >
          {{ placeholder }}
        </view>
        <view class="select-content-item" v-if="multiSelectList.length > 0">
          {{ multiSelectList[0][valueName] }}
        </view>
        <view class="select-content-item" v-if="multiSelectList.length > 1">
          {{ multiLength }}
        </view>
      </view>

      <slot v-if="$slots.icon" name="icon"></slot>
      <span v-else style="width: 10rpx;"></span>

      <input
        v-if="!multiple || filterable"
        class="filter_input"
        type="text"
        @input="inputChange"
        @blur="blurChange"
        :placeholder="
          multiple
            ? multiSelectList.length === 0
              ? placeholder
              : ''
            : placeholder
        "
        :disabled="!filterable"
        :style="!filterable ? 'pointer-events: none' : ''"
        :value="inputData"
      />
      <!-- #ifdef VUE2 -->
      <view
        @click.stop="refreshValue"
        class="close-icon"
        v-if="showClose && (multiple ? value.length > 0 : value)"
      >
        <image :src="refreshUrl" mode="" />
      </view>
      <view
        v-if="value.length <= 0 || !showClose"
        :class="isShow ? 'w-select-arrow-up' : ''"
        class="w-select-arrow"
      />
      <!-- #endif -->
      <!-- #ifdef VUE3 -->
      <view
        @click.stop="refreshValue"
        class="close-icon"
        v-if="showClose && (multiple ? modelValue.length > 0 : modelValue)"
      >
        <image :src="refreshUrl" mode="" />
      </view>
      <view
        v-if="modelValue.length <= 0 || !showClose"
        :class="isShow ? 'w-select-arrow-up' : ''"
        class="w-select-arrow"
      />
      <!-- #endif -->

      <scroll-view
        scroll-y
        v-show="optionsShow"
        :class="[
          isShow
            ? showPosition === 'bottom'
              ? 'animation-bottom-in'
              : 'animation-top-in'
            : showPosition === 'bottom'
            ? 'animation-bottom-out'
            : 'animation-top-out',
          showPosition === 'bottom' ? 'position-bottom' : 'position-top',
        ]"
        class="select-options"
      >
        <!-- #ifdef VUE2 -->
        <view
          @click.stop="handleClickItem(item)"
          :class="
            multiple &&
            multiSelectList.find((res) => res[keyName] === item[keyName])
              ? 'item-active'
              : value === item[keyName]
              ? 'item-active'
              : ''
          "
          v-for="item in filterList"
          :key="item[keyName]"
          class="select-option-item"
        >
          {{ item[valueName] }}
        </view>
        <!-- #endif -->
        <!-- #ifdef VUE3 -->
        <view
          @click.stop="handleClickItem(item)"
          :class="
            multiple &&
            multiSelectList.find((res) => res[keyName] === item[keyName])
              ? 'item-active'
              : modelValue === item[keyName]
              ? 'item-active'
              : ''
          "
          v-for="item in filterList"
          :key="item[keyName]"
          class="select-option-item"
        >
          {{ item[valueName] }}
        </view>
        <!-- #endif -->

        <view class="options-no-data" v-if="filterList.length < 1">
          无匹配数据~
        </view>
      </scroll-view>
    </view>
    <view v-if="isShow" @click="closeContentSelect" class="contentMask" />
  </view>
</template>

<script>
export default {
  props: {
    width: {
      type: String,
      default: "200px",
    },
    height: {
      type: String,
      default: "30px",
    },
    bgColor: {
      type: String,
      default: "#fff",
    },
    // 是否多选
    multiple: {
      type: Boolean,
      default: false,
    },
    // 是否可搜索
    filterable: {
      type: Boolean,
      default: false,
    },
    // 是否显示关闭按钮
    showClose: {
      type: Boolean,
      default: false,
    },
    // 渲染列表
    list: {
      type: Array,
      default: () => [],
    },
    // #ifdef VUE3
    // 双向绑定的值
    modelValue: {
      type: [Array, String, Number],
      default: "",
    },
    // #endif
    // #ifdef VUE2
    // 双向绑定的值
    value: {
      type: [Array, String, Number],
      default: "",
    },
    // #endif
    // 默认显示的内容
    placeholder: {
      type: String,
      default: "请选择",
    },
    // 显示的内容
    valueName: {
      type: String,
      default: "name",
    },
    // 绑定的内容
    keyName: {
      type: String,
      default: "id",
    },
    filterKey: {
      type: String,
      default: "",
    },
  },
  // #ifdef VUE3
  emits: ["update:modelValue", "change"],
  // #endif
  watch: {
    list: {
      immediate: true,
      deep: true,
      handler(news) {
        this.filterList = news;
        const findItem = news.find((item) => {
          let isItem = "";
          // #ifdef VUE3
          if (item[this.keyName] === this.modelValue) {
            isItem = true;
          } else {
            isItem = false;
          }
          // #endif

          // #ifdef VUE2
          if (item[this.keyName] === this.value) {
            isItem = true;
          } else {
            isItem = false;
          }
          // #endif
          return isItem;
        });
        if (findItem) {
          this.inputData = findItem[this.valueName];
        }
      },
    },
    // #ifdef VUE2
    value: {
      handler(newValue) {
        if (this.multiple) {
          this.multiSelectList = newValue || [];
        } else {
          const findItem = this.list.find(
            (it) => it[this.keyName] === newValue
          );
          if (findItem) {
            this.inputData = findItem[this.valueName];
          }
        }
      },
    },
    // #endif
    // #ifdef VUE3
    modelValue: {
      handler(newValue) {
        if (this.multiple) {
          this.multiSelectList = newValue || [];
        } else {
          const findItem = this.list.find(
            (it) => it[this.keyName] === newValue
          );
          if (findItem) {
            this.inputData = findItem[this.valueName];
          }
        }
      },
    },
    // #endif
  },
  computed: {
    multiLength() {
      const length = this.multiSelectList.length - 1;
      return "+" + length;
    },
    bottomDistance() {
      return this.windowHeight - this.distanceTop - this.curHeight; // 当前元素距离可视屏幕底部的距离
    },
  },
  data() {
    return {
      inputData: "",
      multiSelectList: [],
      isShow: false,
      optionsShow: false,
      windowHeight: null,
      curHeight: null,
      distanceTop: null,
      showPosition: "bottom",
      filterList: [],
      refreshUrl:
        "",
    };
  },
  mounted() {
    this.$nextTick(() => {
      const res = uni.getSystemInfoSync();
      this.windowHeight = res.windowHeight; // 当前设备屏幕高度
      uni
        .createSelectorQuery()
        .in(this)
        .select("#wSelect")
        .boundingClientRect((data) => {
          this.distanceTop = data.top; // 当前元素距离顶部的距离
          this.curHeight = data.height;
        })
        .exec();
    });
  },
  methods: {
    showPositon() {
      this.showPosition = "bottom";
      if (this.bottomDistance < this.windowHeight / 3) {
        this.showPosition = "top";
      }
    },
    changeShow() {
      this.isShow = !this.isShow;
      if (this.isShow === false) {
        this.filterList = this.list;
        setTimeout(() => {
          this.optionsShow = false;
        }, 200);
      } else {
        this.showPositon();
        this.optionsShow = this.isShow;
      }
    },
    closeContentSelect() {
      this.isShow = false;
      setTimeout(() => {
        this.optionsShow = false;
      }, 200);
    },
    setValue(value = "") {
      // #ifdef VUE3
      this.$emit("update:modelValue", value);
      // #endif

      // #ifdef VUE2
      this.$emit("input", value);
      // #endif
    },
    inputChange(e) {
      const value = e.detail.value;
      if (this.multiple && this.filterable) {
        this.inputData = value;
      } else {
        this.setValue(value);
        this.inputData = value;
      }

      this.filterList = this.list.filter((item) =>
        item[this.filterKey || this.keyName].includes(value)
      );
    },
    blurChange(e) {
      const value = e.detail.value;

      if (this.multiple && this.filterable && value) {
        const curValue = {
          [this.keyName]: value,
          [this.valueName]: value,
        };
        this.multiSelect(curValue);
      }
    },
    refreshValue() {
      this.setValue("");
      this.inputData = "";
      this.$emit("change", "");
      this.filterList = this.list;
      if (this.multiple) {
        this.multiSelectList = [];
      }
    },
    handleClickItem(e) {
      if (this.multiple) {
        this.multiSelect(e);
      } else {
        this.setValue(e[this.keyName]);
        this.inputData = e[this.valueName];
        this.$emit("change", e);
        this.changeShow();
      }
    },
    multiSelect(item) {
      const index = this.multiSelectList.findIndex(
        (res) => res[this.valueName] === item[this.valueName]
      );
      if (index > -1) {
        this.multiSelectList.splice(index, 1);
      } else {
        this.multiSelectList.push(item);
      }
      this.inputData = "";
      this.filterList = this.list;
      this.setValue(this.multiSelectList);
      this.$emit("change", item);
    },
  },
};
</script>
<style lang="scss" scoped>
.w-select {
  --select-wrap-width: 200px;
  --select-wrap-height: 30px;
  --select-border-radius: 4px;
  --select-border: 1px solid #dcdfe6;
  --select-active-border: 1px solid #409eff;
  --select-options-max-height: 150px;
  --select-option-item-font-size: 14px;
  --select-input-font-size: 15px;
  --no-data-default-color: #999;
  --select-options-box-shadow: 0px 0px 12px rgb(0 0 0 / 12%);
  --select-bg-color: #ffffff;
  .select-wrap {
    position: relative;
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: var(--select-wrap-width);
    height: var(--select-wrap-height);
    border: var(--select-border);
    border-radius: var(--select-border-radius);
    background-color: var(--select-bg-color);
    transition: all 0.2s;
    input {
      padding: 0 2px;
      width: 100%;
      min-width: 0;
      height: 100%;
      font-size: var(--select-input-font-size);
      flex: 1;
      
    }
    .select-content {
      display: flex;
      align-items: center;
      font-size: var(--select-option-item-font-size);
      .select-content-item {
        margin-left: 5px;
        padding: 2px 6px;
        border-radius: var(--select-border-radius);
        color: #aa93b1;
        background-color: #f4f4f5;
      }
      .select-content-item-default {
        margin-left: 5px;
        color: var(--no-data-default-color);
      }
    }
    .close-icon {
      position: absolute;
      top: 50%;
      right: 7px;
      z-index: 1000;
      width: 15px;
      height: 15px;
      transform: translateY(-50%);
      image {
        width: 100%;
        height: 100%;
      }
    }
    .position-bottom {
      top: calc(var(--select-wrap-height) + 10px);
    }
    .position-top {
      bottom: calc(var(--select-wrap-height) + 10px);
    }
    .select-options {
      box-sizing: border-box;
      position: absolute;
      right: 0;
      left: 0;
      z-index: 999;
      overflow: scroll;
      padding: 10px;
      max-height: var(--select-options-max-height);
      border-radius: var(--select-border-radius);
      background-color: var(--select-bg-color);
      box-shadow: var(--select-options-box-shadow);
      .select-option-item {
        margin-bottom: 5px;
        padding: 5px;
        font-size: var(--select-option-item-font-size);
        transition: background-color 0.2s;
      }
      .item-active {
        font-weight: 700;
        color: #409eff;
        background-color: #f5f7fa;
      }
      .options-no-data {
        font-size: var(--select-option-item-font-size);
        text-align: center;
        color: var(--no-data-default-color);
      }
    }
    .w-select-arrow {
      display: inline-block;
      margin: 3px 10px 0;
      width: 8px;
      height: 8px;
      border-top: 1px solid transparent;
      border-right: 1px solid transparent;
      border-bottom: 1px solid #999999;
      border-left: 1px solid #999999;
      transition: all 0.3s;
      transform: translateY(-50%) rotate(-45deg);
    }
    .w-select-arrow-up {
      transform: rotate(-225deg);
    }
  }
  .select-wrap-active {
    border: var(--select-active-border);
  }
  .animation-bottom-in {
    animation-name: bottom-in;
    animation-duration: 0.4s;
    animation-timing-function: ease-out;
    animation-fill-mode: both;
  }
  .animation-bottom-out {
    animation-name: bottom-out;
    animation-duration: 0.2s;
    animation-timing-function: ease-out;
    animation-fill-mode: both;
  }
  .animation-top-in {
    animation-name: top-in;
    animation-duration: 0.4s;
    animation-timing-function: ease-out;
    animation-fill-mode: both;
  }
  .animation-top-out {
    animation-name: top-out;
    animation-duration: 0.2s;
    animation-timing-function: ease-out;
    animation-fill-mode: both;
  }

  @keyframes bottom-in {
    0% {
      opacity: 0;
      transform: translateY(-15%);
    }
    100% {
      opacity: 1;
      transform: translateY(0);
    }
  }

  @keyframes bottom-out {
    0% {
      opacity: 1;
      transform: translateY(0);
    }
    100% {
      opacity: 0;
      transform: translateY(-20%);
    }
  }

  @keyframes top-in {
    0% {
      opacity: 0;
      transform: translateY(15%);
    }
    100% {
      opacity: 1;
      transform: translateY(0);
    }
  }

  @keyframes top-out {
    0% {
      opacity: 1;
      transform: translateY(0);
    }
    100% {
      opacity: 0;
      transform: translateY(20%);
    }
  }
  .contentMask {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 998;
    width: 100%;
    height: 100%;
  }
}
</style>
